#include <iostream>
#include <vector>
#include <stdio.h>
#include <string>
#include <math.h>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <algorithm>
#include <iomanip>
#include <assert.h>

using namespace std;

#define forn( i,n ) for ( int i=0; i<(int)(n); i++ )
// forn : for ( i en [0,n) )
#define fornin(i, desde, hasta) for ( int i=(desde); i<(int)(hasta); i++ )
// fornin : for ( i en [desde,hasta) )
#define sortAll(v) sort((v).begin(), (v).end(), orden);
#define Cuadrado(A) ((A)*(A))
#define absoluto(a) ((a)< 0) ? -(a) : a
#define IZQ 0
#define DER 1
#define ARR 2
#define ABA 3
// Defino las direcciones, cada una corresponde a una pared dada.

#define mas_infinito 1000000000.0L
#define menos_infinito -1000000000.0L


typedef int direccion; // IZQ, DER, ARR, ABA

typedef pair<int, int> punto_int;
typedef pair<int, int> dimensiones; // alto x ancho
typedef pair<long double, long double> segmento;
typedef pair<long double, long double> punto_double;
typedef pair<long double, bool> pendiente;
typedef pair<pendiente, pendiente > pendientes;
typedef vector<segmento>   segmentos;
typedef vector<segmento>::iterator it_segmentos;

direccion las_direcciones[] = {IZQ, DER, ARR, ABA};
string las_direccioens_str[] = {"IZQ", "DER", "ARR", "ABA"};

// --------------------------------------------------------------
// Calcula las dos penditenes de las rectas tangentes que tocan
// un foco y un circulo.
// --------------------------------------------------------------
pendientes calcularPendientes(int r_, punto_int circulo, punto_int foco){
    long long int xc = circulo.first;
    long long int yc = circulo.second;
    long long int xf = foco.first;
    long long int yf = foco.second;
    long long int r = r_;
    if(xc + r != xf && xc - r != xf) {// Caso en que la pendiente es != de infinito
        long double r_ = (long double)r;
        long long unsigned a1 = Cuadrado(xc - xf) + Cuadrado(yc - yf);
        long long unsigned a2 = Cuadrado(r);

        long long unsigned a = a1 - a2;
        long long int b = (xc - xf)*(yc - yf);
        long long int c = (r + xc - xf)*(r - xc + xf);

        long double sqrt_a = r_ * sqrt(a);

        // Ecuaciones para calcular la pendiente
        // Predespejadas y simplificadas.
        long double m1 = (long double)(-b + sqrt_a)/c;
        long double m2 = (long double)(-b - sqrt_a)/c;

        return pendientes(pendiente(m1, true), pendiente(m2, true));
    } else { // Una de las dos pendientes es infinito.
        long double a = Cuadrado(xc) - (2*xc*xf) + Cuadrado(xf) - Cuadrado(yc - yf);
        long double b = 2*(xc - xf)*(yc - yf);

        // Ecuaciones para calcular la pendiente que no es infinito
        long double pen = a/b;
        return pendientes(pendiente(-pen, true), pendiente(0, false));
    }
}

// --------------------------------------------------------------
// Calcula las dos penditenes de las rectas tangentes que tocan
// un foco y un circulo.
// --------------------------------------------------------------
punto_double interseccionCon(direccion dir, punto_int foco, pendiente p, dimensiones dim, punto_int circulo) {
    long double inf;
    if(p.second) {// La pendiente no es infinito
        long double m = p.first;
        switch(dir) {
            // Ecuaciones predespejadas para encontrar la interseccion
            // de las rectas tangentes al circulo que pasan por el foco
            // contra la pared.
            // En caso de tener pendiente no infinita
            case IZQ:
                return punto_double(0, -m * (long double)foco.first + (long double)foco.second);
            case DER:
                return punto_double(dim.second, m*((long double)dim.second - (long double)foco.first) + (long double)foco.second);
            case ARR:
                if(m == 0){
                    (circulo.first < foco.first) ? inf = menos_infinito : inf = mas_infinito ;
                    return punto_double(inf, dim.first);
                } else {
                    return punto_double((long double)foco.first + ((long double)dim.first - (long double)foco.second)/m, dim.first);
				}
            case ABA:
                if(m == 0) {
                    (circulo.first < foco.first) ? inf = menos_infinito : inf = mas_infinito ;
                    return punto_double(inf,0);
                } else {
                    return punto_double(foco.first - ((long double)(foco.second)/m), 0);
				}
        }
    } else {// La pendiente es infinito
        switch(dir) {
            case IZQ:
                (circulo.second < foco.second) ? inf = menos_infinito : inf = mas_infinito;
                return punto_double(0, inf);
			
			case DER:
				(circulo.second < foco.second) ? inf = menos_infinito : inf = mas_infinito;
                return punto_double(dim.second,inf);
                
            case ARR:
                return punto_double(foco.first, dim.first);
            case ABA:
                return punto_double(foco.first, 0);
        }
    }
    return punto_double(0, 0);
}

// --------------------------------------------------------------
// Verifica si un punto toca el contorno del rectangulo
// --------------------------------------------------------------
bool tocaElContorno(punto_double interseccion, dimensiones dimension){
    bool toca_izquierda =   interseccion.first == 0 &&
                            interseccion.second >= 0.0L &&
                            interseccion.second <= (long double)dimension.first ;

    bool toca_derecha =     interseccion.first == (long double)dimension.second  &&
                            interseccion.second >= 0.0L &&
                            interseccion.second <= (long double)dimension.first ;

    bool toca_arriba =      interseccion.second == (long double)dimension.first &&
                            interseccion.first >= 0.0L &&
                            interseccion.first <= (long double)dimension.second ;

    bool toca_abajo =       interseccion.second == 0 &&
                            interseccion.first >= 0.0L &&
                            interseccion.first <= (long double)dimension.second ;

    return toca_izquierda || toca_derecha || toca_abajo || toca_arriba;
}

// --------------------------------------------------------------
// Distancia entre dos puntos.
// --------------------------------------------------------------
long double distanciaEntre(punto_double a, punto_double b) {
    long double distancia = Cuadrado(a.first - b.first) + Cuadrado(a.second - b.second);
    return sqrt(distancia);
}

// --------------------------------------------------------------
// Verifica si el punto 'interseccion' es necesario para calcular
// las sombras. (Solo los que salen del foco hacia una pared y tocan el circulo).
// --------------------------------------------------------------
bool interesa(punto_double interseccion, punto_int foco, punto_int circulo, dimensiones dimension, int radio){
    long double radio_ = (long double)radio;
    bool toca_contorno = tocaElContorno(interseccion, dimension);

    // Obtengo la distancia del centro del circulo al segmento
    // formado por el foco y la pared.

    punto_double a = punto_double((long double)foco.first, (long double)foco.second);
    punto_double c = punto_double((long double)circulo.first, (long double)circulo.second);
    punto_double b = interseccion;

    bool toca_el_circulo = false;
    long double u = ( (c.first - a.first)*(b.first - a.first) +
                    (c.second - a.second)*(b.second - a.second))/(Cuadrado(distanciaEntre(b,a)));

    if(u >= -0.0001L && u <= 1.0001L){
        long double numerador = (b.first - a.first)*(c.second - a.second) -
                          (b.second - a.second)*(c.first - a.first);
        long double dist_entre = distanciaEntre(b, a);

        // Ecuacion para calcular la distancia minima
        // de un punto a un segmento dado.
        long double distancia = absoluto(numerador/dist_entre);

        long double dif = 0.00001L;

        // En caso de que la distancia sea igual al radio.
        // Significa que el segmento toca el circulo.
        toca_el_circulo =  distancia >= radio_*(1.0L-dif) && distancia <= radio_*(1.0L+dif); // ver si es +- .005%
    }

    // interesa sólo si toca el circulo y el contorno
    return toca_contorno && toca_el_circulo;
}

// --------------------------------------------------------------
// La pendiente del segmento foco hacia la pared.
// --------------------------------------------------------------
long double pendienteALaPared(punto_double foco, punto_double pared){
    // forma de calcular la pendiente de una recta dado dos puntos.
    return (foco.second - pared.second)/(foco.first - pared.first) ;
}

// --------------------------------------------------------------
// Dado un segmento, [foco --> pared], decide si el circulo
// esta en sentido horario o no al segmento.
// --------------------------------------------------------------
bool estaEnSentidoHorario(punto_double centro_circulo, punto_double foco, punto_double pared, int radio_){
    long double radio = (long double)radio_;
    if(pared.first == foco.first) {// En el caso de la pendiente sea Infinito
        if(centro_circulo.first + radio == foco.first) return pared.second < foco.second;
        if(centro_circulo.first - radio == foco.first) return pared.second > foco.second;
        return false;
    } else {// Si la pendiente no es infinito.
        long double pendiente = pendienteALaPared(foco, pared);
        long double y_en_recta = pendiente*(centro_circulo.first - pared.first) + pared.second;

        return  (foco.first < pared.first && y_en_recta >= centro_circulo.second ) ||
                (foco.first > pared.first && y_en_recta <= centro_circulo.second ) ;
    }
}

// --------------------------------------------------------------
// Dado dos puntos en uno de los bordes, un circulo y un foco.
// Devuelve en caso de existir, un segmento de sombra.
// --------------------------------------------------------------
segmento interseccionCon(direccion dir, punto_double intersec_1, punto_double intersec_2, punto_int circulo_, punto_int foco_, dimensiones dimension, int radio_){
    // Me dan los dos puntos de interseccion de las rectas
    // generdas contra una sola pared.

    long double radio = (long double)radio_;
    punto_double circulo = punto_double((long double)circulo_.first, (long double)circulo_.second);
    punto_double foco = punto_double((long double)foco_.first, (long double)foco_.second);

    // Verifico si los puntos son de interes.
    bool interesa_primero = interesa(intersec_1, foco_, circulo_, dimension, radio_);
    bool interesa_segundo = interesa(intersec_2, foco_, circulo_, dimension, radio_);

    bool interesan_ambos = interesa_primero && interesa_segundo;
    punto_double el_que_interesa, el_que_no_interesa;

    // En el caso de que no me interese ninguno
    // Puede ser que se deba completar
    // todo el segmento con una sombra.
    // Ya que ninguna recta toca el contorno pero si genera sombra.
    if(!interesa_primero && !interesa_segundo){

        bool izq_der = (intersec_1.second <= 0 && intersec_2.second >= dimension.first) ||
                       (intersec_2.second <= 0 && intersec_1.second >= dimension.first);
        bool arr_aba = (intersec_1.first <= 0 && intersec_2.first >= dimension.second) ||
                       (intersec_2.first <= 0 && intersec_1.first >= dimension.second);
        switch(dir) {
            case IZQ:
                if(circulo.first + radio <= foco.first && izq_der && intersec_1.first == 0 && intersec_2.first == 0)
                    return segmento(0, dimension.first);
                break;
            case DER:
                if(circulo.first - radio >= foco.first && izq_der && intersec_1.first == dimension.second && intersec_2.first == dimension.second)
                    return segmento(0, dimension.first);
                break;
            case ARR:
                if(circulo.second - radio >= foco.second && arr_aba && intersec_1.second == dimension.first && intersec_2.second == dimension.first)
                    return segmento(0, dimension.second);
                break;
            case ABA:
                if(circulo.second + radio <= foco.second && arr_aba && intersec_1.second == 0 && intersec_2.second == 0)
                    return segmento(0, dimension.second);
                break;
        }
        return segmento(0,0); // Verk onda!
    }

    // Si interesa uno solo verifico cual es
    if(!interesan_ambos) {
        if(interesa_primero) {
            el_que_interesa = intersec_1;
            el_que_no_interesa = intersec_2;
        } else if(interesa_segundo) {
            el_que_interesa = intersec_2;
            el_que_no_interesa = intersec_1;
        }
    }

    // Si interesan ambos busco el segmento entre ellos dos.
    if(interesan_ambos) {
        switch(dir) {
            case IZQ:
            case DER:
                return segmento(min(intersec_1.second, intersec_2.second), max(intersec_1.second, intersec_2.second));
            case ABA:
            case ARR:
                return segmento(min(intersec_1.first, intersec_2.first), max(intersec_1.first, intersec_2.first));
        }
    } else {
        bool esta_en_sentido_horario = estaEnSentidoHorario(circulo, foco, el_que_interesa,radio_);

        switch(dir) {
            case IZQ:
                if(esta_en_sentido_horario){
                    return segmento(el_que_interesa.second, dimension.first);
                } else {
                    return segmento(0, el_que_interesa.second);
				}
            case ARR:
				if(esta_en_sentido_horario) {
                    return segmento(el_que_interesa.first, dimension.second);
				} else {
                    return segmento(0, el_que_interesa.first);
				}
            case ABA:
				if(esta_en_sentido_horario) {
                    return segmento(0, el_que_interesa.first);
				} else {
                    return segmento(el_que_interesa.first, dimension.second);
				}
            case DER:
                if(esta_en_sentido_horario) {
                    return segmento(0, el_que_interesa.second);
                } else {
                    return segmento(el_que_interesa.second, dimension.first);
				}
        }
    }
    return segmento(0,0);
}

// --------------------------------------------------------------
// Funcion de orden para ordenar segmentos con std
// --------------------------------------------------------------
bool orden(segmento seg1, segmento seg2) {
    // Ordeno primero por el primero y despues por el segundo.
    return seg1.first < seg2.first  ||
            (seg1.first == seg2.first && seg1.second <= seg2.second);
}

// --------------------------------------------------------------
// Union de segmentos.
// --------------------------------------------------------------
segmentos unionSegmentos(segmentos los_segmentos) {
    segmentos res = segmentos();

    // Union de vacio es vacio.
    if(los_segmentos.size() == 0) return res;

    // Agrego el primer elemento
    res.push_back(*los_segmentos.begin());

    it_segmentos seg;
    for(seg = los_segmentos.begin(); seg!=los_segmentos.end(); ++seg){
        if(seg != los_segmentos.begin()) {
            // Para cada segmento menos el primero.
            // Si interseca con el anterior agrando el anterior
            // y en caso contrario lo agrego.
            segmento &ultimo = res[res.size() -1];
            if (ultimo.second < seg->first) {// No interseca con el anterior.
                res.push_back(*seg);
            } else {// interseca con el anterior.
                ultimo = segmento(ultimo.first, max(seg->second, ultimo.second));
			}
        }
    }
    return res;
}

// --------------------------------------------------------------
// Complemento de segmentos en un rango.
// --------------------------------------------------------------
segmentos complemento(segmentos los_segmentos, segmento intervalo_completo) {
    // Precondicion : Asumo que estan ordenados y no se intersecan entre si.

    segmentos res = segmentos();

    // El complemento de vacio es el intervalo completo.
    if(los_segmentos.size() == 0) {
        res.push_back(segmento(intervalo_completo.first, intervalo_completo.second));
        return res;
    }

    // Si el primero no empieza en el principio del intervalo
    // agrego el segmento del principio al intervalo.
    segmento primero = *los_segmentos.begin();
    if(primero.first > intervalo_completo.first) {
        segmento actual_primero = segmento(intervalo_completo.first, primero.first);
        res.push_back(actual_primero);
    }
    
    segmento ultimo = los_segmentos[los_segmentos.size() - 1];

    it_segmentos seg;
    for(seg = los_segmentos.begin(); seg!=los_segmentos.end(); ++seg){
        if(*seg != ultimo) {
            // Para cada par de segmento
            // Agrego el segmento formado por la ultima parte
            // del primero y la primera parte del segundo.
            segmento siguiente = *(++seg);
            seg--;
            segmento actual_medio = segmento(seg->second, siguiente.first);
            res.push_back(actual_medio);
        }
    }

    // En caso de que el ultimo elemento no coincida con
    // el final del intervalo. Agrego lo que falta.
    if(ultimo.second < intervalo_completo.second) {
        segmento actual_fin = segmento(ultimo.second, intervalo_completo.second);
        res.push_back(actual_fin);
    }
    return res;
}

// --------------------------------------------------------------
// El intervalo completo dado uno de los costados. 0 a (alto o ancho)
// --------------------------------------------------------------
segmento intervaloCompleto(direccion dir,dimensiones dim) {
    switch(dir) {
        case IZQ:
        case DER:
            return segmento(0, dim.first);
        case ARR:
        case ABA:
            return segmento(0, dim.second);
    }
    return segmento(0,0);
}


// --------------------------------------------------------------
// Dado las sombras precalculadas para un foco/circulo
// Completa algunos de los costados de ser necesario ya que
// La sombra tiene que ser continua.
// --------------------------------------------------------------
void completarSegmentosDeSombra(segmento (&seg)[4], dimensiones dim)
{
    bool debo_completar = false;

    bool arr_izq = seg[ARR].first != -1 && seg[ARR].first == 0;
    bool aba_izq = seg[ABA].first != -1 && seg[ABA].first == 0;
    bool arr_der = seg[ARR].first != -1 && seg[ARR].second == dim.second;
    bool aba_der = seg[ABA].first != -1 && seg[ABA].second == dim.second;

    bool izq_arr = seg[IZQ].first != -1 && seg[IZQ].second == dim.first;
    bool izq_aba = seg[IZQ].first != -1 && seg[IZQ].first == 0;
    bool der_arr = seg[DER].first != -1 && seg[DER].second == dim.first;
    bool der_aba = seg[DER].first != -1 && seg[DER].first == 0;

    // las sombras generadas deben ser continuas.
    // En el caso de que se hayan agregado algunas sombras
    // Pero alguna pared entre dos sombras no este completa = >la agrego.
    forn(dir,4) {
        if(seg[dir].first == -1 && seg[dir].second == -1) {
            switch(dir) {
                // Para cada pared se deben cumplir condiciones distintas.
                case ARR:
                    debo_completar = (izq_arr && der_arr) || (izq_arr && aba_der) || (der_arr && aba_izq);
                    if(debo_completar) seg[dir] = segmento(0, dim.second);
                    break;
                    
                case ABA:
                    debo_completar = (izq_aba && der_aba) || (izq_aba && arr_der) || (der_aba && arr_izq);
                    if(debo_completar) seg[dir] = segmento(0, dim.second);
                    break;
                    
                case IZQ:
                    debo_completar = (arr_izq && aba_izq) || (arr_izq && der_aba) || (aba_izq && der_arr);
                    if(debo_completar) seg[dir] = segmento(0, dim.first);
                    break;
                    
                case DER:
                    debo_completar = (arr_der && aba_der) || (arr_der && izq_aba) || (aba_der && izq_arr);
                    if(debo_completar) seg[dir] = segmento(0, dim.first);
                    break;
            }
        }
    }
}

// --------------------------------------------------------------
// main
// --------------------------------------------------------------
int main()
{
    string fin = "";
    int cantidad_focos, cantidad_circulos, alto, ancho;
    while(cin >> cantidad_focos >> cantidad_circulos >> ancho >> alto) {
        // Condicion de frenado.
        if(cantidad_focos == 0 && cantidad_circulos == 0 && ancho == 0 && alto == 0)
            break;

        // Levanto los datos iniciales.
        dimensiones dimension(alto , ancho);

        // Levanto los focos.
        vector<punto_int> focos(cantidad_focos);
        forn(i, cantidad_focos) {
            int x, y;
            cin >> x >> y;
            focos[i] = punto_int(x, y);
        }

        // Levanto los circulos con sus radios.
        vector<punto_int> circulos(cantidad_circulos);
        vector<int> radios(cantidad_circulos);
        forn(i, cantidad_circulos) {
            int x, y, radio;
            cin >> x >> y >> radio;
            circulos[i] = punto_int(x, y);
            radios[i] = radio;
        }

        // Inicializo en vacio el segmento con todas las luces.
        segmentos los_segmentos_luz[4] = {segmentos(), segmentos(), segmentos(), segmentos()};

        // Calculo la luz generada para cada foco con todos los circulos.
        forn(un_foco, cantidad_focos){
            punto_int foco = focos[un_foco];

            // Inicializo los segmentos de sombra generados por los circulos
            // para un foco dado.
            segmentos los_segmentos_sombra[4] = {segmentos(), segmentos(), segmentos(), segmentos()};

            forn(un_circulo, cantidad_circulos) {
                segmento los_segmentos_actuales[4] = {segmento(-1,-1),segmento(-1,-1),segmento(-1,-1),segmento(-1,-1)};
                // Para un foco y un circulo :

                // Calculo las pendientes de las rectas tangentes
                pendientes las_pendientes = calcularPendientes(radios[un_circulo], circulos[un_circulo], foco);

                // Obtengo las 8 intersecciones de las rectas que
                // pasan por el foco con dichas pendientes y las cuatro rectas
                // de las paredes.
                punto_double intersecciones[4][2];
                forn(dir, 4) {
                    intersecciones[dir][0] = interseccionCon(dir, foco, las_pendientes.first , dimension, circulos[un_circulo]);
                    intersecciones[dir][1] = interseccionCon(dir, foco, las_pendientes.second, dimension, circulos[un_circulo]);
                }

                // Para cada pared obtengo si se genera
                // el segmento de sombra y lo agrego al conjunto.
                forn(dir, 4) {
                    segmento s = interseccionCon(dir, intersecciones[dir][0], intersecciones[dir][1], circulos[un_circulo], foco, dimension, radios[un_circulo]);
                    if(s.first < s.second)
                        los_segmentos_actuales[dir] = s;
                }

                // Agrego en caso de ser necesarios paredes completas de sombras.
                completarSegmentosDeSombra(los_segmentos_actuales, dimension);

                forn(dir, 4) {
                    segmento s = los_segmentos_actuales[dir];
                    if(s.first != -1 && s.second != -1)
                        los_segmentos_sombra[dir].push_back(s);
                }
            }


            // Dados los segmentos de sombra de un foco y todos los circulos.
            // Obtengo los segmentos de luz como el complemento.
            forn(dir, 4) {
                segmentos &seg_sombra = los_segmentos_sombra[dir];

                // Ordeno las sombras y las uno.
                sortAll(seg_sombra);
                seg_sombra = unionSegmentos(seg_sombra);

                // Obtengo los segmentos de luz como el complemento de la sombra.
                segmento intervalo_completo = intervaloCompleto(dir, dimension);
                segmentos segmentos_luz = complemento(seg_sombra, intervalo_completo);

                // Concateno los segmentos resultante a los ya obtenidos para otros focos.
                segmentos &seg_luz = los_segmentos_luz[dir];
                seg_luz.insert(seg_luz.end(), segmentos_luz.begin(), segmentos_luz.end());

            }
        }

        // Calculo la luz total
        // Como suma de los segmentos de luz.
        long double luz_total = 0.0L;

        forn(dir, 4) {
            segmentos &seg_luz = los_segmentos_luz[dir];

            // Ordeno los segmentos de luz y hago la union
            sortAll(seg_luz);
            seg_luz = unionSegmentos(seg_luz);

            // Itero todos los segmentos de luz y voy sumando
            // los intervalos al total
            it_segmentos s;
            for(s = seg_luz.begin(); s!=seg_luz.end(); ++s) {
                luz_total += - s->first + s->second ;
            }
        }

        // Devuelvo el resultado.
        cout << fin;
        cout << setiosflags(ios::fixed) << setprecision(4)<< luz_total;
        fin = "\n";
    }
    return 0;
}
